/********************************************************************
# Copyright 2017 Daniel 'grindhold' Brendle
#
# This file is part of parceldude.
#
# parceldude is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either
# version 3 of the License, or (at your option) any later
# version.
#
# parceldude is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with parceldude.
# If not, see http://www.gnu.org/licenses/.
*********************************************************************/

namespace Parceldude {
    public interface FeatureContainer {
        public abstract void add_feature(Feature f);
        public abstract string get_paramstring();
    }

    [GtkTemplate (ui = "/de/grindhold/parceldude/ui/installer.ui")]
    public class Installer : Gtk.Box, FeatureContainer {
        public string filepath {get; private set; default="";}

        public HashTable<string, FeatureData?> features;

        private bool analyzed = false;

        [GtkChild]
        private Gtk.Label filename;

        [GtkChild]
        private Gtk.ProgressBar progress;

        public Gtk.Box featurelist {get; private set;}

        public Installer(string path) {
            this.features = new HashTable<string, FeatureData?>(str_hash, str_equal);
            this.filepath = path;
            this.filename.label = path;
            this.featurelist = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);

#if HAS_LIBMSI
            try {
                new GLib.Thread<int>.try("parse", this.parse_features);
            } catch (Error e) {
                message("Could not start Thread: %s", e.message);
            }
#else
            this.analyzed = true;
            this.progress.hide();
#endif
              
            this.show_all();
        }

        [GtkCallback]
        private void delete_entry() {
            this.parent.destroy();
        }

        public void add_feature(Feature f) {
            this.featurelist.add(f);
        }

        public bool is_analyzed() {
            bool ret = false;
            lock(this.analyzed) {
                ret = this.analyzed;
            }
            return ret;
        }

        public string get_paramstring() {
            string result = "";
            this.featurelist.@foreach((w)=>{
                string ps = (w as Feature).get_paramstring();
                result += result.length != 0 && ps.length != 0 ? "," : "";
                result += ps;
            });
            return result;
        }

#if HAS_LIBMSI
        public void build_feature_ui() {
            this.features.@foreach((id, featuredata) => {
                this.add_feature(new Feature(featuredata));
            });
        }

        private int parse_features() {
            try {
                GLib.Timeout.add(100, ()=>{
                    this.progress.pulse();
                    return !this.is_analyzed();
                });
                var db = new Libmsi.Database(this.filepath, Libmsi.DbFlags.READONLY, null);
                var q = new Libmsi.Query(db, "select * from `Feature`");

                var parsed = new HashTable<string, FeatureData?>(str_hash, str_equal);

                while (true) {
                    var r = q.fetch();
                    if (r == null) break;

                    var featuredata = FeatureData();

                    featuredata.id = r.get_string(Feature.ID);
                    featuredata.name = r.get_string(Feature.NAME);
                    featuredata.description = r.get_string(Feature.DESCRIPTION);
                    featuredata.parent_id = r.get_string(Feature.PARENT);

                    parsed.set(featuredata.id, featuredata);
                    if (featuredata.parent_id == null || featuredata.parent_id == "") {
                        this.features.set(featuredata.id, featuredata);
                    }
                }

                uint assigned_features = 1;
                while (assigned_features > 0) {
                    assigned_features = 0;
                    List<string> to_remove = new List<string>();
                    parsed.@foreach((key, f)=> {
                        if (f.parent_id in this.features) {
                            this.features.@get(f.parent_id).children.append(f);
                            to_remove.append(key);
                            assigned_features++;
                        }
                    });
                    to_remove.@foreach((key)=>{
                        parsed.remove(key);
                    });
                }
                GLib.Idle.add(()=>{
                    lock (this.analyzed) {
                        this.analyzed = true;
                    }
                    this.progress.hide();
                    this.build_feature_ui();
                    return false;
                });
                return 0;
            } catch (Error e) {
                message("Could not open archive: %s", e.message);
                this.delete_entry();
                return 1;
            }

        }
#endif
    }
}
